#include "mmu.h"
#include "i386.h"

unsigned int proximalibre_kernel;
unsigned int proximalibre_usuario;

void inicializar_mmu(){
	proximalibre_kernel=INICIO_PAGINAS_KERNEL;
	proximalibre_usuario=INICIO_PAGINAS_USUARIO;
}

unsigned int* pagina_libre_kernel(){
	int pagina_libre = proximalibre_kernel;
	
	if (pagina_libre>=INICIO_PAGINAS_USUARIO)
		return 0;
	else {	
		proximalibre_kernel += TAMANO_PAGINA;
		return (unsigned int*)pagina_libre;
	}
}

unsigned int* pagina_libre_usuario(){
	int pagina_libre = proximalibre_usuario;
	
	if (proximalibre_usuario>=MAX_MEM)
		return 0;
	else {
		proximalibre_usuario+= TAMANO_PAGINA;
		return (unsigned int*)pagina_libre;
	}
}

unsigned int* inicializar_dir_kernel(){ 	// Devuelve la direccion del directorio.
	int i;
	// La primer pagina que obtengo es el directorio.
	unsigned int *pagina_dir = pagina_libre_kernel();
	
	// La segunda pagina es la tabla.
	unsigned int *pagina_actual = pagina_libre_kernel();
	*pagina_dir = (unsigned int)pagina_actual | 3;

	// Inicializamos la tabla de paginas.
	for (i=0;i<1024;i++) {
		int indice = i;
		*pagina_actual = (indice << 12) | 3;
		pagina_actual++;
	}
	return pagina_dir;
}


void extender_dir_kernel(){
	int i;
	
	// La primer pagina que obtengo es el directorio.
	unsigned int *pagina_dir = (unsigned int*)INICIO_PAGINAS_KERNEL;
	
	// Salteamos la primer entrada del directorio.
	pagina_dir++;
	
	unsigned int pagina_actual = TAMANO_PAGINA_4MB;	
	
	// Inicializamos las 15 posiciones siguientes del directorio(con paginas de 4mb).
	for (i=0;i<15;i++) {
		*pagina_dir = pagina_actual | 0x83;
		pagina_dir++;
		pagina_actual += TAMANO_PAGINA_4MB;
	}
}

unsigned int* inicializar_dir_usuario(){ 	// Devuelve la direccion del directorio.
	int i;
	// La primer pagina que obtengo es el directorio.
	unsigned int *pagina_dir = pagina_libre_kernel();	
	
	// La segunda pagina es la tabla.
	unsigned int *pagina_actual = pagina_libre_kernel();
	*pagina_dir = (unsigned int)pagina_actual | 7;

	// Inicializamos la tabla de paginas.
	for (i=0;i<256;i++) {
		int indice = i;
		*pagina_actual = (indice << 12) | 3;
		pagina_actual++;
	}
	return pagina_dir;
}

int esta_presente(unsigned int dir){
	return (dir && 0x00000001);
}

unsigned int obtengo_directorio(unsigned int virtual){
	unsigned int res = virtual >> 22;
	res = res & 0x000003FF;
	return res*4;
}

unsigned int obtengo_tabla(unsigned int virtual){
	unsigned int res = virtual >> 12;
	res = res & 0x000003FF;
	return res*4;
}

void mapear_pagina(unsigned int virtual, unsigned int cr3, unsigned int fisica,unsigned int atributos){
	
	unsigned int directorio = obtengo_directorio(virtual);
	unsigned int tabla = obtengo_tabla(virtual);

	unsigned int *ptr_dir = (unsigned int *)(cr3 + directorio);
	unsigned int *ptr_dir_tabla;
	
	unsigned int pte = fisica & 0xFFFFF000;
	atributos = atributos & 0x00000FFF;
	
	pte = pte | atributos;
	
	if (esta_presente(*ptr_dir)){
		ptr_dir_tabla = (unsigned int *)((unsigned int)(*ptr_dir & 0xFFFFF000) + tabla);
		*ptr_dir_tabla = pte;
	} else {
		ptr_dir_tabla = pagina_libre_kernel();
		*ptr_dir = (unsigned int)ptr_dir_tabla;
		ptr_dir_tabla = (unsigned int *)((unsigned int)*ptr_dir + tabla);
		*ptr_dir = *ptr_dir | 7;
		*ptr_dir_tabla = pte;
	}
	tlbflush();
}

void unmapear_pagina(unsigned int virtual, unsigned int cr3){
	mapear_pagina(virtual,cr3,0x00000000,0x00000000);
}

unsigned int obtener_virtual_libre(unsigned int *cr3){
	unsigned int dir_cr3 = (unsigned int)cr3 & 0xFFFFF000;
	unsigned int ptr_tabla_paginas = *((unsigned int *)dir_cr3) & 0xFFFFF000;
	ptr_tabla_paginas += 1024;
	int counter_dir = 0;
	int counter_tab = 256;
	while(counter_dir<1024){
		if(!esta_presente(*((unsigned int *)dir_cr3 + counter_dir*4))){
			counter_dir = counter_dir<<22;
			return counter_dir;
		}
		
		while (counter_tab<1024){
			if (!esta_presente(*((unsigned int *)ptr_tabla_paginas))){
				counter_dir = counter_dir<<22;
				counter_tab = counter_tab<<12;
				unsigned int res = counter_dir | counter_tab;
				return res;
			}
			ptr_tabla_paginas += 4;
			counter_tab++;
		}
		
		counter_tab = 0;
		counter_dir++;
		ptr_tabla_paginas = *((unsigned int *)(dir_cr3 + counter_dir*4));
		
	}
	
	return 0;
}





/*
#define INICIO_PAGINAS_KERNEL 	0x00100000
#define INICIO_PAGINAS_USUARIO	0x00180000 
#define TAMANO_PAGINA 			0x00001000 =  4 KB.
#define TAMANO_PAGINA_4MB		0x00400000 =  4 MB.
#define MAX_MEM					0x04000000 = 64 MB.
*/
